package show.marvelous.domain.chessgame.aggregate;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Optional;
import lombok.Data;
import lombok.NonNull;
import org.jilt.Builder;
import org.jilt.BuilderStyle;
import org.jilt.Opt;
import show.marvelous.domain.chessgame.aggregate.entity.DiceRecord;
import show.marvelous.domain.chessgame.aggregate.entity.MoveEvent;
import show.marvelous.domain.chessgame.aggregate.entity.PlayRecord;
import show.marvelous.domain.chessgame.aggregate.entity.PlayRecordBuilder;
import show.marvelous.domain.chessgame.aggregate.entity.Position;
import show.marvelous.domain.chessgame.aggregate.entity.PositionBuilder;
import show.marvelous.domain.chessgame.constans.AirPlanColorEnum;
import show.marvelous.domain.chessgame.constans.PieceCodeEnum;
import show.marvelous.domain.chessgame.constans.PieceEnum;
import show.marvelous.domain.chessgame.constans.TakeOffStrategyEnum;

/**
 * The type Game.
 * @author Kris 2021/8/24
 */
@Data
public class ChessGame{

    public static final int PUBLIC_POSITION_COUNT = 12;
    private final Long id;
    private final Collection<Position> positions;
    private final Collection<DiceRecord> diceRecords;
    private final Collection<MoveEvent> moveEvents;
    private final TakeOffStrategyEnum takeOffStrategy;

    @Builder(style = BuilderStyle.TYPE_SAFE)
    public ChessGame(@NonNull Long id, @Opt Collection<DiceRecord> diceRecords, @Opt Collection<MoveEvent> moveEvents,
            @NonNull TakeOffStrategyEnum takeOffStrategy){
        this.id = id;
        // 初始化位置信息
        Collection<Position> positions = new ArrayList<>();
        for(AirPlanColorEnum color : AirPlanColorEnum.values()){
            Position ceilingPosition = PositionBuilder.position().color(color).index(Position.CEILING_INDEX).build();
            for(PieceCodeEnum code : PieceCodeEnum.values()){
                ceilingPosition.gotPiece(PieceEnum.of(color, code));
            }
            positions.add(ceilingPosition);
            positions.add(PositionBuilder.position().color(color).index(Position.TAKE_OFF_INDEX).build());
            positions.add(PositionBuilder.position().color(color).index(Position.END_INDEX).build());
            for(int i = 0; i <= PUBLIC_POSITION_COUNT; i++){
                positions.add(PositionBuilder.position().color(color).index(4 * i + 2).build());
            }
        }
        // 初始化棋子信息
        for(PieceEnum piece : PieceEnum.values()){
            Optional<MoveEvent> lastMoveEvent = moveEvents.stream().filter(m -> piece == m.getPiece())
                    .max(Comparator.comparingLong(MoveEvent::getId));
            int positionIndex =
                    lastMoveEvent.isPresent() ? lastMoveEvent.get().getPositionIndex() : Position.CEILING_INDEX;
            getPosition(piece.getColor(), positionIndex).gotPiece(piece);
        }
        this.positions = positions;
        this.diceRecords = Optional.ofNullable(diceRecords).orElse(new ArrayList<>());
        this.moveEvents = Optional.ofNullable(moveEvents).orElse(new ArrayList<>());
        this.takeOffStrategy = takeOffStrategy;
    }

    public DiceRecord shakeDice(){
        Optional<DiceRecord> unusedDiceRecord = getUnusedDiceRecord();
        if(unusedDiceRecord.isPresent()){
            return unusedDiceRecord.get();
        }else{
            DiceRecord diceRecord = new DiceRecord(IdUtil.getSnowflake().nextId(), getCurPlayer());
            diceRecords.add(diceRecord);
            return diceRecord;
        }
    }

    private Optional<DiceRecord> getUnusedDiceRecord(){
        if(CollectionUtil.isEmpty(diceRecords)){
            return Optional.empty();
        }
        return diceRecords.stream().filter(DiceRecord::isUnused).findFirst();
    }

    public PlayRecord buildPlayRecord(PieceCodeEnum code){
        Optional<DiceRecord> unusedDiceRecord = getUnusedDiceRecord();
        if(!unusedDiceRecord.isPresent()){
            throw new IllegalStateException("你还没摇骰子");
        }
        DiceRecord diceRecord = unusedDiceRecord.get();
        return PlayRecordBuilder.playRecord().id(IdUtil.getSnowflake().nextId()).code(code).diceRecord(diceRecord)
                .build();
    }

    /**
     * Move piece optional.
     *
     * @param playRecord the play record
     * @return the optional
     */
    public Optional<MoveEvent> play(PlayRecord playRecord){
        final Optional<Position> position = positions.stream()
                .filter(p -> p.containPiece(playRecord.getColor(), playRecord.getCode())).findFirst();
        if(position.isPresent()){
            return position.get()
                    .moveOut(playRecord.getColor(), playRecord.getCode(), playRecord.getStep(), takeOffStrategy);
        }else{
            throw new IllegalArgumentException(
                    "数据错误，棋盘上找不到棋子：" + playRecord.getColor().getColorName() + playRecord.getCode().name());
        }
    }

    /**
     * Happen collection.
     *
     * @param moveEvent the move event
     * @return the collection
     */
    public Collection<MoveEvent> happen(MoveEvent moveEvent){
        Position position = getPosition(moveEvent.getPositionColor(), moveEvent.getPositionIndex());
        this.moveEvents.add(moveEvent);
        return position.handleMoveEvent(moveEvent);
    }

    private Position getPosition(AirPlanColorEnum color, Integer index){
        return positions.stream().filter(p -> p.isSamePosition(color, index)).findFirst()
                .orElseThrow(() -> new IllegalArgumentException("找不到位置" + color.getColorName() + index));
    }

    /**
     * Get id long.
     *
     * @return the long
     */
    public Long getId(){
        return id;
    }

    /**
     * Get positions iterator.
     *
     * @return the iterator
     */
    public Iterator<Position> getPositions(){
        return positions.iterator();
    }

    /**
     * Get cur player air plan color enum.
     *
     * @return the air plan color enum
     */
    public AirPlanColorEnum getCurPlayer(){
        Optional<DiceRecord> lastDiceRecord = diceRecords.stream().max(Comparator.comparingLong(DiceRecord::getId));
        if(lastDiceRecord.isPresent()){
            DiceRecord diceRecord = lastDiceRecord.get();
            if(diceRecord.isUnused() || diceRecord.getStep().equals(DiceRecord.MAX_STEP)){
                return diceRecord.getColor();
            }else{
                return diceRecord.getColor().nextColor();
            }
        }else{
            return AirPlanColorEnum.RED;
        }
    }
}
